Resynchronizing Jittery AES Power Traces

What happens if things aren't as clean as we made them out to be? We can use preprocessing modules!

In [1]:
SCOPETYPE = 'OPENADC'
PLATFORM = 'CWLITEXMEGA'
CRYPTO_TARGET = 'AVRCRYPTOLIB'
num_traces = 100
CHECK_CORR = False

Capturing Jittery Traces

Rebuilding New Firmware

In file chipwhisperer/hardware/victims/firmware/simpleserial-aes/simpleserial-aes.c find this:

uint8_t get_pt(uint8_t* pt)
{
    trigger_high();
    aes_indep_enc(pt); /* encrypting the data block */
    trigger_low();
    simpleserial_put('r', 16, pt);
    return 0x00;
}

and add some random delay:

uint8_t get_pt(uint8_t* pt)
{
    trigger_high();
       for(volatile uint8_t k = 0; k < (*pt & 0x0F); k++);
    aes_indep_enc(pt); /* encrypting the data block */
    trigger_low();
    simpleserial_put('r', 16, pt);
    return 0x00;
}

This deterministic delay is NOT a good countermeasure, but is much easier to write in a single line since we don’t have a CSPRNG linked in. We’ll break the jitter without relying on the deterministic aspect though, so our attack would work against a better jitter source.

Be sure to remove this function afterwards so you don't break your code!

We can build the code (change the platform as needed), and confirm the output of the following works as you expect:

In [2]:
%%bash -s "$PLATFORM" "$CRYPTO_TARGET"
cd ../hardware/victims/firmware/simpleserial-aes
make PLATFORM=$1 CRYPTO_TARGET=$2 EXTRA_OPTS=ADD_JITTER
rm -f -- simpleserial-aes-CWLITEXMEGA.hex
rm -f -- simpleserial-aes-CWLITEXMEGA.eep
rm -f -- simpleserial-aes-CWLITEXMEGA.cof
rm -f -- simpleserial-aes-CWLITEXMEGA.elf
rm -f -- simpleserial-aes-CWLITEXMEGA.map
rm -f -- simpleserial-aes-CWLITEXMEGA.sym
rm -f -- simpleserial-aes-CWLITEXMEGA.lss
rm -f -- objdir/*.o
rm -f -- objdir/*.lst
rm -f -- simpleserial-aes.s simpleserial.s XMEGA_AES_driver.s uart.s usart_driver.s xmega_hal.s aes-independant.s aes_enc.s aes_keyschedule.s aes_sbox.s aes128_enc.s
rm -f -- simpleserial-aes.d simpleserial.d XMEGA_AES_driver.d uart.d usart_driver.d xmega_hal.d aes-independant.d aes_enc.d aes_keyschedule.d aes_sbox.d aes128_enc.d
rm -f -- simpleserial-aes.i simpleserial.i XMEGA_AES_driver.i uart.i usart_driver.i xmega_hal.i aes-independant.i aes_enc.i aes_keyschedule.i aes_sbox.i aes128_enc.i
.
-------- begin --------
avr-gcc (GCC) 5.4.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

.
Compiling C: simpleserial-aes.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial-aes.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/simpleserial-aes.o.d simpleserial-aes.c -o objdir/simpleserial-aes.o 
.
Compiling C: .././simpleserial/simpleserial.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/simpleserial.o.d .././simpleserial/simpleserial.c -o objdir/simpleserial.o 
.
Compiling C: .././hal/xmega/XMEGA_AES_driver.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/XMEGA_AES_driver.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/XMEGA_AES_driver.o.d .././hal/xmega/XMEGA_AES_driver.c -o objdir/XMEGA_AES_driver.o 
.
Compiling C: .././hal/xmega/uart.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/uart.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/uart.o.d .././hal/xmega/uart.c -o objdir/uart.o 
.
Compiling C: .././hal/xmega/usart_driver.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/usart_driver.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/usart_driver.o.d .././hal/xmega/usart_driver.c -o objdir/usart_driver.o 
.
Compiling C: .././hal/xmega/xmega_hal.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/xmega_hal.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/xmega_hal.o.d .././hal/xmega/xmega_hal.c -o objdir/xmega_hal.o 
.
Compiling C: .././crypto/aes-independant.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes-independant.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/aes-independant.o.d .././crypto/aes-independant.c -o objdir/aes-independant.o 
.
Compiling C: .././crypto/avrcryptolib//aes/aes_enc.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes_enc.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/aes_enc.o.d .././crypto/avrcryptolib//aes/aes_enc.c -o objdir/aes_enc.o 
.
Compiling C: .././crypto/avrcryptolib//aes/aes_keyschedule.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes_keyschedule.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/aes_keyschedule.o.d .././crypto/avrcryptolib//aes/aes_keyschedule.c -o objdir/aes_keyschedule.o 
.
Compiling C: .././crypto/avrcryptolib//aes/aes_sbox.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes_sbox.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/aes_sbox.o.d .././crypto/avrcryptolib//aes/aes_sbox.c -o objdir/aes_sbox.o 
.
Compiling C: .././crypto/avrcryptolib//aes/aes128_enc.c
avr-gcc -c -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes128_enc.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/aes128_enc.o.d .././crypto/avrcryptolib//aes/aes128_enc.c -o objdir/aes128_enc.o 
.
Assembling: .././crypto/avrcryptolib//gf256mul/gf256mul.S
avr-gcc -c -mmcu=atxmega128d3 -I. -x assembler-with-cpp -DF_CPU=7372800 -Wa,-gstabs,-adhlns=objdir/gf256mul.lst -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul .././crypto/avrcryptolib//gf256mul/gf256mul.S -o objdir/gf256mul.o
.
Linking: simpleserial-aes-CWLITEXMEGA.elf
avr-gcc -mmcu=atxmega128d3 -I. -DADD_JITTER -fpack-struct -gdwarf-2 -DSS_VER=SS_VER_1_1 -DHAL_TYPE=HAL_xmega -DPLATFORM=CWLITEXMEGA -DAVRCRYPTOLIB -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial-aes.o -I.././simpleserial/ -I.././hal -I.././hal/xmega -I.././crypto/ -I.././crypto/avrcryptolib//aes -I.././crypto/avrcryptolib//gf256mul -std=gnu99 -MMD -MP -MF .dep/simpleserial-aes-CWLITEXMEGA.elf.d objdir/simpleserial-aes.o objdir/simpleserial.o objdir/XMEGA_AES_driver.o objdir/uart.o objdir/usart_driver.o objdir/xmega_hal.o objdir/aes-independant.o objdir/aes_enc.o objdir/aes_keyschedule.o objdir/aes_sbox.o objdir/aes128_enc.o objdir/gf256mul.o --output simpleserial-aes-CWLITEXMEGA.elf -Wl,-Map=simpleserial-aes-CWLITEXMEGA.map,--cref   -lm  
.
Creating load file for Flash: simpleserial-aes-CWLITEXMEGA.hex
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature simpleserial-aes-CWLITEXMEGA.elf simpleserial-aes-CWLITEXMEGA.hex
.
Creating load file for EEPROM: simpleserial-aes-CWLITEXMEGA.eep
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 --no-change-warnings -O ihex simpleserial-aes-CWLITEXMEGA.elf simpleserial-aes-CWLITEXMEGA.eep || exit 0
.
Creating Extended Listing: simpleserial-aes-CWLITEXMEGA.lss
avr-objdump -h -S -z simpleserial-aes-CWLITEXMEGA.elf > simpleserial-aes-CWLITEXMEGA.lss
.
Creating Symbol Table: simpleserial-aes-CWLITEXMEGA.sym
avr-nm -n simpleserial-aes-CWLITEXMEGA.elf > simpleserial-aes-CWLITEXMEGA.sym
Size after:
   text	   data	    bss	    dec	    hex	filename
   3264	     32	    228	   3524	    dc4	simpleserial-aes-CWLITEXMEGA.elf
+--------------------------------------------------------
+ Built for platform CW-Lite XMEGA
+--------------------------------------------------------

Setup

Now let's go ahead. We'll have to program the file we built, so be sure to confirm we are using the right file!

In [3]:
%run "Helper_Scripts/Setup.ipynb"
In [4]:
import os, time

fw_path = '../hardware/victims/firmware/simpleserial-aes/simpleserial-aes-{}.hex'.format(PLATFORM)

modtime = os.path.getmtime(fw_path)
print("File build time: {:s} (built {:.2f} mins ago)".format(str(time.ctime(modtime)), (time.time() - modtime)/60.0))
File build time: Fri Jul 12 17:05:35 2019 (built 0.02 mins ago)
In [5]:
cw.program_target(scope, prog, fw_path)
XMEGA Programming flash...
XMEGA Reading flash...
Verified flash OK, 3295 bytes

In addition, before we capture our traces, we'll need to create a ChipWhipserer project, since that's what Analyzer expects for an input:

In [6]:
project = cw.create_project("jupyter_test_jittertime", overwrite = True)

Capturing Traces

Below you can see the capture loop. The main body of the loop loads some new plaintext, arms the scope, sends the key and plaintext, then finally records and our new trace into our trace class. We'll also keep track of our keys manually for checking our answer later.

In [7]:
#Capture Traces
from tqdm import tnrange
import numpy as np
import time

ktp = cw.ktp.Basic()

target.init()
for i in tnrange(num_traces, desc='Capturing traces'):
    key, text = ktp.next()  # manual creation of a key, text pair can be substituted here
    trace = cw.capture_trace(scope, target, text, key)
    if trace is None:
        continue
    project.traces.append(trace)

In [8]:
#Save project file
project.save()

We're now done with the ChipWhisperer hardware, so we should disconnect from the scope and target:

In [9]:
# cleanup the connection to the target and scope
scope.dis()
target.dis()

Analysis

To fix the jitter, we'll need to add our traces to a preprocessing module. We can feed project.traceManager() right into attack.setTraceSource(), but we could also add pre-processing inbetween (more about this later). We'll also re-open the traces, in this case it is required since the call to closeAll() would have flushed the buffers.

In [10]:
import chipwhisperer as cw
import chipwhisperer.analyzer as cwa
project = cw.open_project("jupyter_test_jittertime")

This time we're going to do a few things. First we will get the traces, and plot a few of them as-is. You can adjust the traces plotted by adjusting the range(10). For example range(1) plots the first trace.

In [11]:
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.palettes import Dark2_5 as palette
import itertools  

output_notebook()
p = figure(sizing_mode='scale_width', plot_height=300)

# create a color iterator
colors = itertools.cycle(palette)  

x_range = range(0, len(project.waves[0]))
for i, color in zip(range(5), colors): #Adjust range(n) to plot certain traces
    p.line(x_range, project.waves[i], color=color)
show(p)
Loading BokehJS ...

So how do we fix that? To begin with, you should plot only a single trace to make your life more clear. You'll need to figure out a very unique area. For example see the following figure showing a single plot. In this example the location of
A is unique, but B would have many matches within that same trace, even nearby: Resync example trace

We will specify two items:

  • A window with the "unique" area defined.
  • How far we will shift the window (+/- points) to search for the best match.

You can use the following code to define the target_window and max_shift. Try a few values until you find something that works. Also try some poor example, and also try plotting more traces to confirm your match is working in real life.

In [12]:
resync_traces = cwa.preprocessing.ResyncSAD(project)
resync_traces.ref_trace = 0

if PLATFORM == "CWNANO":
    #Define a target window here. 500,900 for example is good based on above. But try some different values.
    resync_traces.target_window = (300, 700)

    # Define max_shift. Must not cause target_window to go outside of valid data. Try 16-600 range. Ideal value varies with how
    # much jitter is in original data. 
    resync_traces.max_shift = 300
elif PLATFORM == "CWLITEXMEGA" or PLATFORM == "CW303":
    #Define a target window here. 500,900 for example is good based on above. But try some different values.
    resync_traces.target_window = (1000, 1400)

    # Define max_shift. Must not cause target_window to go outside of valid data. Try 16-600 range. Ideal value varies with how
    # much jitter is in original data. 
    resync_traces.max_shift = 1000
else:
    #Define a target window here. 500,900 for example is good based on above. But try some different values.
    resync_traces.target_window = (700, 1500)

    # Define max_shift. Must not cause target_window to go outside of valid data. Try 16-600 range. Ideal value varies with how
    # much jitter is in original data. 
    resync_traces.max_shift = 700

#Uses objects from previous cells (plotting etc), so 
output_notebook()
p = figure()
new_proj = resync_traces.preprocess()

for i, color in zip(range(0, 5), colors):
    p.line(x_range, new_proj.waves[i], color=color)
show(p)
Loading BokehJS ...

If this all works - let's just continue the attack! Do so as below:

In [13]:
leak_model = cwa.leakage_models.sbox_output
attack = cwa.cpa(new_proj, leak_model)
ERROR:root:Value <chipwhisperer.analyzer.attacks.cpa_algorithms.progressive.CPAProgressive object at 0x000001E7482DA208> out of limits ({}) in parameter "Attack Algorithm"
ERROR:root:Value <chipwhisperer.analyzer.attacks.cpa_algorithms.progressive.CPAProgressive object at 0x000001E7482DA208> out of limits ({}) in parameter "Attack Algorithm"

And then actually run it:

In [14]:
cb = cwa.get_jupyter_callback(attack)
attack_results = attack.run(cb, 10)
Finished traces 90 to 100
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
PGE= 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 2B
0.877
7E
0.876
15
0.883
16
0.911
28
0.667
AE
0.873
D2
0.840
A6
0.906
AB
0.629
F7
0.798
15
0.917
88
0.875
09
0.677
CF
0.770
4F
0.804
3C
0.869
1 2A
0.637
7F
0.612
14
0.619
17
0.560
29
0.629
AF
0.584
D3
0.685
A7
0.588
AC
0.468
F6
0.601
14
0.615
89
0.649
08
0.671
CE
0.574
4E
0.587
3D
0.616
2 82
0.502
E0
0.459
B3
0.507
5E
0.468
67
0.493
7B
0.481
8D
0.457
ED
0.475
80
0.467
0C
0.468
56
0.453
B9
0.467
F1
0.451
4A
0.464
8C
0.472
21
0.478
3 41
0.492
DE
0.433
A5
0.449
4C
0.467
CD
0.444
2A
0.460
86
0.443
73
0.468
C6
0.458
D1
0.461
9F
0.445
FE
0.466
03
0.448
A6
0.444
0A
0.447
37
0.473
4 FF
0.456
77
0.429
73
0.448
25
0.457
78
0.443
BD
0.457
AC
0.440
6A
0.464
41
0.451
2F
0.450
32
0.429
F8
0.452
92
0.448
74
0.441
47
0.439
6E
0.468

You should see the PGE reach 0 for each byte. If not, you might need to adjust the SAD resync. You could also need to increase the length of the sample capture for example. You may notice that it starts working OK and then fails, due to later traces become unsychronized.

Plotting Correlation Output

In [15]:
from bokeh.plotting import figure, show
from bokeh.io import output_notebook

attack_results = attack.results
plot_data = cwa.analyzer_plots(attack_results)
bnum = 0

ret = plot_data.output_vs_time(bnum)

output_notebook()
p = figure()
p.line(ret[0], ret[2], line_color='green')
p.line(ret[0], ret[3], line_color='green')

p.line(ret[0], ret[1], line_color='red')
show(p)
Loading BokehJS ...

You should see a graph of red and green in time (samples). In red is the correlation of the correct subkey for the first byte, while the rest are in green.

You should see two or three distinctive red spikes. The first is the spot where the sbox lookup for the subkey we guessed actually happens (the later ones are from later steps in the AES operation).

What about the rest of the bytes in the key? We can get and plot that easily as well:

In [16]:
rets = []
for i in range(0, 16):
    rets.append(plot_data.output_vs_time(i))

p = figure()
for ret in rets:
    p.line(ret[0], ret[2], line_color='green')
    p.line(ret[0], ret[3], line_color='green')
    
for ret in rets:
    p.line(ret[0], ret[1], line_color='red')

show(p)

Conclusion

Awesome! You should have now completed a resynchronization of power traces. This is a very useful tool, and you can see how making a simple class could extend this work.

Tests

In [17]:
key = list(project.keys[0])
recv_key = [kguess[0][0] for kguess in attack_results.find_maximums()]
assert key == recv_key, "Failed to recover encryption key\nGot: {}\nExpected: {}".format(recv_key, key)
In [18]:
assert (attack_results.pge == [0]*16), "PGE for some bytes not zero: {}".format(attack_results.pge)
In [19]:
if CHECK_CORR:
    max_corrs = [kguess[0][2] for kguess in attack_results.find_maximums()]
    assert (np.all([corr > 0.75 for corr in max_corrs])), "Low correlation in attack (corr <= 0.75): {}".format(max_corrs)
In [ ]: